package wci.frontend.pascal.parsers;

import static wci.frontend.pascal.PascalErrorCode.IDENTIFIER_UNDEFINED;
import static wci.frontend.pascal.PascalErrorCode.INCOMPATIBLE_TYPES;
import static wci.frontend.pascal.PascalErrorCode.INVALID_FIELD;
import static wci.frontend.pascal.PascalErrorCode.INVALID_IDENTIFIER_USAGE;
import static wci.frontend.pascal.PascalErrorCode.MISSING_RIGHT_BRACKET;
import static wci.frontend.pascal.PascalErrorCode.TOO_MANY_SUBSCRIPTS;
import static wci.frontend.pascal.PascalTokenType.COMMA;
import static wci.frontend.pascal.PascalTokenType.DOT;
import static wci.frontend.pascal.PascalTokenType.EQUALS;
import static wci.frontend.pascal.PascalTokenType.IDENTIFIER;
import static wci.frontend.pascal.PascalTokenType.LEFT_BRACKET;
import static wci.frontend.pascal.PascalTokenType.RIGHT_BRACKET;
import static wci.frontend.pascal.PascalTokenType.SEMICOLON;
import static wci.intermediate.icodeimpl.ICodeKeyImpl.ID;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.FIELD;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.SUBSCRIPTS;
import static wci.intermediate.symtabimpl.DefinitionImpl.UNDEFINED;
import static wci.intermediate.symtabimpl.DefinitionImpl.VALUE_PARM;
import static wci.intermediate.symtabimpl.DefinitionImpl.VARIABLE;
import static wci.intermediate.symtabimpl.DefinitionImpl.VAR_PARM;
import static wci.intermediate.typeimpl.TypeFormImpl.ARRAY;
import static wci.intermediate.typeimpl.TypeFormImpl.RECORD;
import static wci.intermediate.typeimpl.TypeKeyImpl.ARRAY_ELEMENT_TYPE;
import static wci.intermediate.typeimpl.TypeKeyImpl.ARRAY_INDEX_TYPE;
import static wci.intermediate.typeimpl.TypeKeyImpl.RECORD_SYMTAB;

import java.util.EnumSet;

import wci.frontend.Token;
import wci.frontend.TokenType;
import wci.frontend.pascal.PascalParserTD;
import wci.frontend.pascal.PascalTokenType;
import wci.intermediate.Definition;
import wci.intermediate.ICodeFactory;
import wci.intermediate.ICodeNode;
import wci.intermediate.SymTab;
import wci.intermediate.SymTabEntry;
import wci.intermediate.TypeForm;
import wci.intermediate.TypeSpec;
import wci.intermediate.icodeimpl.ICodeNodeTypeImpl;
import wci.intermediate.symtabimpl.Predefined;
import wci.intermediate.typeimpl.TypeChecker;

/**
 * <h1>VariableParser</h1>
 * <p>解析一个Pascal变量</p>
 * <p>Copyright (c) 2009 by Ronald Mak</p>
 * <p>For instructional purposes only.  No warranties.</p>
 */
public class VariableParser extends StatementParser
{
	private boolean isFunctionTarget = false;
    public VariableParser(PascalParserTD parent)
    {
        super(parent);
    }

    // 在下标[]或字段a.b处的同步集
    private static final EnumSet<PascalTokenType> SUBSCRIPT_FIELD_START_SET =
        EnumSet.of(LEFT_BRACKET, DOT);

   
    public ICodeNode parse(Token token)
        throws Exception
    {
        // 在栈中查找此变量
        String name = token.getText().toLowerCase();
        SymTabEntry variableId = symTabStack.lookup(name);

        // 如果没有找到，报错，且补偿一个未定义
        if (variableId == null) {
            errorHandler.flag(token, IDENTIFIER_UNDEFINED, this);
            variableId = symTabStack.enterLocal(name);
            variableId.setDefinition(UNDEFINED);
            variableId.setTypeSpec(Predefined.undefinedType);
        }

        return parse(token, variableId);
    }

	public ICodeNode parseFunctionNameTarget(Token token) throws Exception {
		isFunctionTarget = true;
		return parse(token);
	}
    /**
     * 解析一个变量
     * @param token 当前token
     * @param variableId 变量符号表项
     * @return 分析生成树的根节点
     * @throws Exception
     */
    public ICodeNode parse(Token token, SymTabEntry variableId)
        throws Exception
    {
        // 检查变量是怎样被定义的，有三种可能
        Definition defnCode = variableId.getDefinition();
        if ((defnCode != VARIABLE) && (defnCode != VALUE_PARM) &&
            (defnCode != VAR_PARM))
        {
            errorHandler.flag(token, INVALID_IDENTIFIER_USAGE, this);
        }
        variableId.appendLineNumber(token.getLineNumber());
        //变量根节点
        ICodeNode variableNode =
            ICodeFactory.createICodeNode(ICodeNodeTypeImpl.VARIABLE);
        variableNode.setAttribute(ID, variableId);

        token = nextToken();
        //看是否是有下标或字段[或者.
        TypeSpec variableType = variableId.getTypeSpec();
        while (SUBSCRIPT_FIELD_START_SET.contains(token.getType())) {
            ICodeNode subFldNode = token.getType() == LEFT_BRACKET
                                          ? parseSubscripts(variableType)
                                          : parseField(variableType);
            token = currentToken();

            //更新变量节点的类型，比如a是个对象，那么a.b有可能就是一个整数
            variableType = subFldNode.getTypeSpec();
            variableNode.addChild(subFldNode);
        }

        variableNode.setTypeSpec(variableType);
        return variableNode;
    }

    // 下标结束符的同步集
    private static final EnumSet<PascalTokenType> RIGHT_BRACKET_SET =
        EnumSet.of(RIGHT_BRACKET, EQUALS, SEMICOLON);

    /**
     * 解析一个逗号隔开的下标集，注意Pascal支持a[1,2]这种下标格式，类似C语言的a[1][2]
     * @param variableType 数组变量类型
     * @return 生成分析树的根节点
     * @throws Exception
     */
    private ICodeNode parseSubscripts(TypeSpec variableType)
        throws Exception
    {
        Token token;
        ExpressionParser expressionParser = new ExpressionParser(this);
        TypeSpec currentVarType = variableType;
        //创建一个下标节点
        ICodeNode subscriptsNode = ICodeFactory.createICodeNode(SUBSCRIPTS);

        do {
            token = nextToken(); //吞掉第一个的[或后续的,

            //传入的变量格式一定是数组才行
            if (currentVarType.getForm() == ARRAY) {

                // 解析下标表达式，这儿只有1个
                ICodeNode exprNode = expressionParser.parse(token);
                TypeSpec exprType = exprNode != null ? exprNode.getTypeSpec()
                                                     : Predefined.undefinedType;

                // 下标表达式必须与数组变量的索引类型可以赋值兼容
                TypeSpec indexType =
                    (TypeSpec) currentVarType.getAttribute(ARRAY_INDEX_TYPE);
                if (!TypeChecker.areAssignmentCompatible(indexType, exprType)) {
                    errorHandler.flag(token, INCOMPATIBLE_TYPES, this);
                }

                // 下标包含表达下标的表达式节点作为子几点，即arr[1+2]，那么1+2将作为arr的子节点
                subscriptsNode.addChild(exprNode);

                // 更新当前变量的类型，以便下标的下标，如[1,2]。
                currentVarType =
                    (TypeSpec) currentVarType.getAttribute(ARRAY_ELEMENT_TYPE);
            }

            //不是一个数组，用下标就不对了
            else {
                errorHandler.flag(token, TOO_MANY_SUBSCRIPTS, this);
                expressionParser.parse(token);
            }

            token = currentToken();
        } while (token.getType() == COMMA);

        // 同步在下标结束符]
        token = synchronize(RIGHT_BRACKET_SET);
        if (token.getType() == RIGHT_BRACKET) {
            token = nextToken();  
        }
        else {
            errorHandler.flag(token, MISSING_RIGHT_BRACKET, this);
        }

        subscriptsNode.setTypeSpec(currentVarType);
        return subscriptsNode;
    }

    /**
     * 解析一个记录字段，比如a.b，那么b就是record a里面的一个字段，这个解析每次只能解析一个字段，比如嵌套的<br>
     * a.b.c，a是一个记录,a.b也是一个记录，那么会有两次调用。
     * @param variableType 记录类型的变量
     * @return 分析树的根节点
     * @throws Exception
     */
    private ICodeNode parseField(TypeSpec variableType)
        throws Exception
    {
        //创建一个Field节点
        ICodeNode fieldNode = ICodeFactory.createICodeNode(FIELD);
        TypeSpec currentVarType = variableType;
        Token token = nextToken();  //a.b处的.被吞掉，到b处
        TokenType tokenType = token.getType();
        TypeForm variableForm = currentVarType.getForm();
        //必须是作用在记录上的字段标识符才有意义
        if ((tokenType == IDENTIFIER) && (variableForm == RECORD)) {
            SymTab symTab = (SymTab) currentVarType.getAttribute(RECORD_SYMTAB);
            String fieldName = token.getText().toLowerCase();
            //在记录中的符号表中去查找
            SymTabEntry fieldId = symTab.lookup(fieldName);

            if (fieldId != null) {
                currentVarType = fieldId.getTypeSpec();
                fieldId.appendLineNumber(token.getLineNumber());

                // 设置节点的ID属性为字段表项
                fieldNode.setAttribute(ID, fieldId);
            }
            else {
                errorHandler.flag(token, INVALID_FIELD, this);
            }
        }
        else {
            errorHandler.flag(token, INVALID_FIELD, this);
        }

        token = nextToken();  //吞掉字段

        fieldNode.setTypeSpec(currentVarType);
        return fieldNode;
    }
}
